package cn.demoncat.util.mybatis.plus.base;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.*;
import cn.demoncat.util.lang.constant.MysqlConstant;
import cn.demoncat.util.lang.constant.StatusConstant;
import cn.demoncat.util.lang.entity.MysqlQuery;
import cn.demoncat.util.lang.entity.Pager;
import cn.demoncat.util.lang.entity.Result;
import cn.demoncat.util.mybatis.constant.MapperConstant;
import cn.demoncat.util.mybatis.param.PageNumSize;
import cn.demoncat.util.mybatis.plus.MybatisPlusUtil;
import cn.demoncat.util.mybatis.plus.Query;
import cn.demoncat.util.mybatis.plus.Update;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * BaseService
 *
 * @author 延晓磊
 * @since 2021年08月23日
 */
public interface BaseService<T> extends IService<T> {

    /**
     * 创建PO实例
     *
     * @return
     *
     * @author 延晓磊
     * @since 2022年05月23日
     */
    T instanceEntity();

    /**
     * 批量添加（解决saveBatch循环插入速度慢的问题，需要XML支持）
     *
     * 1、如果赋值ID，使用自定义ID；如果ID为null，使用数据库自增
     * 2、添加后可以通过o.getId()获取主键
     * 3、如果使用INSERT INTO，插入重复时抛异常；如果使用INSERT IGNORE INTO 插入重复时更新
     *
     * @param os
     * @return  是否插入，列表为空是返回false
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    boolean batchSave(List<T> os);

    /**
     * 批量添加（解决saveBatch循环插入速度慢的问题，需要XML支持）
     *
     * 1、如果赋值ID，使用自定义ID；如果ID为null，使用数据库自增
     * 2、添加后可以通过o.getId()获取主键
     * 3、如果使用INSERT INTO，插入重复时抛异常；如果使用INSERT IGNORE INTO 插入重复时更新
     *
     * @param os
     * @param size  批次数量
     * @return  是否插入，列表为空是返回false
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    boolean batchSave(List<T> os, int size);

    /**
     * 添加 InsertIgnore
     *
     * @param o 如果存在（主键、唯一索引）则忽略，否则添加
     * @return
     *
     * @author 延晓磊
     * @since 2022年02月08日
     */
    boolean ignoreSave(T o);

    /**
     * 批量添加 InsertIgnore
     *
     * @param os 如果存在（主键、唯一索引）则忽略，否则添加
     * @return
     *
     * @author 延晓磊
     * @since 2022年02月08日
     */
    boolean ignoreSaveBatch(Collection<T> os);

    /**
     * 添加 ReplaceInto
     *
     * @param o 如果存在（主键、唯一索引）则更新（跨多条数据时合并为1条），否则添加
     * @return
     *
     * @author 延晓磊
     * @since 2022年02月08日
     */
    boolean replaceSave(T o);

    /**
     * 批量添加 ReplaceInto
     *
     * @param os 如果存在（主键、唯一索引）则更新（跨多条数据时合并为1条），否则添加
     * @return
     *
     * @author 延晓磊
     * @since 2022年02月08日
     */
    boolean replaceSaveBatch(Collection<T> os);

    /**
     * 清空
     *
     * @return
     */
    void truncate();

    /**
     * 删除
     *
     * @param id
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean delete(Serializable id){
        return this.removeById(id);
    }

    /**
     * 删除全部
     *
     * @return
     *
     * @author 延晓磊
     * @since 2022年07月28日
     */
    default boolean removeAll(){
        return this.remove(Query.empty());
    }

    /**
     * 删除
     *
     * @param id
     * @param createUser    校验归属用户
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean removeByUser(Serializable id, Long createUser){
        ParamEmptyUtil.checkNull(id, createUser);
        return this.remove(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.CREATE_USER, createUser));
    }

    /**
     * 删除
     *
     * @param id
     * @param orgId    校验归属企业
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean removeByOrg(Serializable id, Long orgId){
        ParamEmptyUtil.checkNull(id,orgId);
        return this.remove(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.ORG_ID, orgId));
    }

    /**
     * 删除
     *
     * @param id
     * @param shopId    校验归属店铺
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean removeByShop(Serializable id, Long shopId){
        ParamEmptyUtil.checkNull(id, shopId);
        return this.remove(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.SHOP_ID, shopId));
    }

    /**
     * 删除并返回
     *
     * @param id
     * @return 删除的数据，数据不存在/删除失败返回null
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T removeAndGet(Serializable id) {
        // 查询
        T o = this.getById(id);
        if (o != null) {
            // 删除
            if (this.delete(id)) {
                return o;
            }
        }
        return null;
    }

    /**
     * 假删除
     *
     * @param id    以id为条件，修改isDel=1
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateDel(Serializable id){
        ParamEmptyUtil.checkNull(id);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.IS_DEL, StatusConstant.Common.YES).eq(MapperConstant.ColumnName.ID, id));
    }

    /**
     * 假删除
     *
     * @param id    以id为条件，修改isDel=1
     * @param createUser    校验归属用户
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateDelByUser(Serializable id, Long createUser){
        ParamEmptyUtil.checkNull(id, createUser);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.IS_DEL, StatusConstant.Common.YES).eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.CREATE_USER, createUser));
    }

    /**
     * 假删除
     *
     * @param id    以id为条件，修改isDel=1
     * @param orgId    校验归属企业
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateDelByOrg(Serializable id, Long orgId){
        ParamEmptyUtil.checkNull(id, orgId);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.IS_DEL, StatusConstant.Common.YES).eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.ORG_ID, orgId));
    }

    /**
     * 假删除
     *
     * @param id    以id为条件，修改isDel=1
     * @param shopId    校验归属店铺
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateDelByShop(Serializable id, Long shopId){
        ParamEmptyUtil.checkNull(id, shopId);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.IS_DEL, StatusConstant.Common.YES).eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.SHOP_ID, shopId));
    }

    /**
     * 撤销假删除
     *
     * @param id    以id为条件，修改isDel=0
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateUnDel(Serializable id){
        ParamEmptyUtil.checkNull(id);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.IS_DEL, StatusConstant.Common.NO).eq(MapperConstant.ColumnName.ID, id));
    }

    /**
     * ID更新
     *
     * SQL：UPDATE table SET field=? WHERE id=?
     *
     * @param entity    忽略null属性，以id为条件
     * @return
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default boolean update(T entity) {
        return this.updateById(entity);
    }

    /**
     * 条件更新
     *
     * 场景：状态变更的锁，首个进入的会加锁数据库并更新，后进入的等待（事务超时、更新失败）
     *
     * @param entity    修改参数，如果id非空将拼接到wrapper中
     * @param query     条件
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean update(T entity, QueryWrapper<T> query) {
        // 拼接id条件
        if (query.getExpression().getNormal().stream().noneMatch(s -> MapperConstant.ColumnName.ID.equals(s.getSqlSegment()))) {
            // 条件中不包含id，判断entity是否指定id
            Object id = ReflectFieldUtil.get(entity, MapperConstant.FieldName.ID);
            if (StringUtil.isNotBlank(id)) {
                // 拼接id
                query.eq(MapperConstant.ColumnName.ID, id);
            }
        }
        return SqlHelper.retBool(this.getBaseMapper().update(entity, query));
    }

    /**
     * 乐观锁更新
     *
     * @param o
     * @return 版本号匹配，递增版本号并返回true
     *
     * @author 延晓磊
     * @since 2023年05月17日
     */
    default boolean updateVersion(T o){
        // 获取版本号
        Field field = ReflectFieldUtil.getField(o.getClass(), MapperConstant.FieldName.VERSION);
        long version = Long.parseLong(ReflectFieldUtil.get(o, field).toString());
        // 设置版本号
        ReflectFieldUtil.set(o, field, version + 1);
        // 条件更新
        return this.update(o, new Query<T>().eq(MapperConstant.ColumnName.VERSION, version));
    }

    /**
     * 乐观锁更新(根据version进行CAS更新；无限循环)
     *
     * 1、设置事务为READ_COMMITTED，方便重试时可重复读取数据： @Transactional( isolation = Isolation.READ_COMMITTED)
     * 2、调用本方法
     * 3、循环times次，查询数据、通过task修改数据、更新数据（自动递增并判断version），超次数未更新成功时抛异常：BizRuntimeException.BUSY
     * 4、表中必须包含必须包含Long version字段，未找到将抛异常；更新时自动+1，不要手动处理
     *
     * @param id	ID查询
     * @param task	对查询到的数据进行更新（设置更新值）
     *
     * @return 更新结果数据
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T update(Serializable id, Consumer<T> task) {
        return this.update(id, task, Integer.MAX_VALUE);
    }

    /**
     * 乐观锁更新(根据version进行CAS更新)
     *
     * 1、设置事务为READ_COMMITTED，方便重试时可重复读取数据： @Transactional( isolation = Isolation.READ_COMMITTED)
     * 2、调用本方法
     * 3、循环times次，查询数据、通过task修改数据、更新数据（自动递增并判断version），超次数未更新成功时抛异常：BizRuntimeException.BUSY
     * 4、表中必须包含必须包含Long version字段，未找到将抛异常；更新时自动+1，不要手动处理
     *
     * @param id	ID查询
     * @param task	对查询到的数据进行更新（设置更新值）
     * @param times	重试次数（默认无限次，超过次数未修改成功时抛异常）
     *
     * @return 更新结果数据
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T update(Serializable id, Consumer<T> task, int times){
        ParamEmptyUtil.checkNull(id, task);
        // 循环，每10次休眠100秒
        T o;
        int sleep = 0;
        long version;
        Field field = null;
        for (int i = 0; i < times; i++) {
            // 查询数据（子类可以通过重写getById来实现不同的结果，如不存在时初始化）
            o = this.getById(id);
            if (o == null) {
                throw ParamRuntimeException.DATA_NULL;
            }
            // 修改数据
            task.accept(o);
            // 更新version
            if (field == null) {
                field = ReflectFieldUtil.getField(o.getClass(), MapperConstant.FieldName.VERSION);
            }
            version = Long.parseLong(ReflectFieldUtil.get(o, field).toString());
            ReflectFieldUtil.set(o, field, version + 1);
            // 乐观锁更新
            if (this.update(o, new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.VERSION, version))) {
                // 成功
                return o;
            }else{
                // 失败，重试
                if (sleep > 10) {
                    // 休眠
                    sleep = 0;
                    ThreadUtil.sleepMillis(MapperConstant.REPEAT_SLEEP);
                }else{
                    sleep ++;
                }
            }
        }
        // 繁忙
        throw BizRuntimeException.BUSY;
    }

    /**
     * 修改状态
     *
     * @param id    以id为条件
     * @param value 字段status的值
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateStatus(Serializable id, Object value){
        ParamEmptyUtil.checkNull(id, value);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.STATUS, value).eq(MapperConstant.ColumnName.ID, id));
    }

    /**
     * 修改排序
     *
     * @param id    以id为条件
     * @param value 字段sort的值
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default boolean updateSort(Serializable id, Object value){
        ParamEmptyUtil.checkNull(id, value);
        return this.update(new Update<T>().set(MapperConstant.ColumnName.SORT, value).eq(MapperConstant.ColumnName.ID, id));
    }


    /**
     * ID查询
     *
     * @param id
     * @param createUser    校验归属用户
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T getByUser(Serializable id, Long createUser){
        ParamEmptyUtil.checkNull(id, createUser);
        return this.getOne(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.CREATE_USER, createUser));
    }

    /**
     * ID查询
     *
     * @param id
     * @param orgId    校验归属企业
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T getByOrg(Serializable id, Long orgId){
        ParamEmptyUtil.checkNull(id, orgId);
        return this.getOne(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.ORG_ID, orgId));
    }

    /**
     * ID查询
     *
     * @param id
     * @param shopId    校验归属店铺
     * @return
     *
     * @author 延晓磊
     * @since 2021年10月11日
     */
    default T getByShop(Serializable id, Long shopId){
        ParamEmptyUtil.checkNull(id, shopId);
        return this.getOne(new Query<T>().eq(MapperConstant.ColumnName.ID, id).eq(MapperConstant.ColumnName.SHOP_ID, shopId));
    }

    /**
     * 查询并处理数据
     *
     * @param id		ID
     * @param handler	fn(原数据)
     * @return
     *
     * @author 延晓磊
     *
     * @since 2020年8月10日
     */
    default Result handle(Serializable id, Function<T,Result> handler) {
        // 查询原数据
        T dbData = this.getById(id);
        if (dbData == null) {
            return Result.ERROR_DATA_NULL;
        }
        // 处理
        return handler.apply(dbData);
    }

    /**
     * 查询并处理数据
     *
     * @param id		ID
     * @param createUser    校验归属用户
     * @param handler	fn(原数据)
     * @return
     *
     * @author 延晓磊
     *
     * @since 2020年8月10日
     */
    default Result handleByUser(Serializable id, Long createUser, Function<T,Result> handler) {
        // 查询原数据
        T dbData = this.getByUser(id, createUser);
        if (dbData == null) {
            return Result.ERROR_DATA_NULL;
        }
        // 处理
        return handler.apply(dbData);
    }

    /**
     * 查询并处理数据
     *
     * @param id		ID
     * @param orgId    校验归属企业
     * @param handler	fn(原数据)
     * @return
     *
     * @author 延晓磊
     *
     * @since 2020年8月10日
     */
    default Result handleByOrg(Serializable id, Long orgId, Function<T,Result> handler) {
        // 查询原数据
        T dbData = this.getByOrg(id, orgId);
        if (dbData == null) {
            return Result.ERROR_DATA_NULL;
        }
        // 处理
        return handler.apply(dbData);
    }

    /**
     * 查询并处理数据
     *
     * @param id		ID
     * @param shopId    校验归属店铺
     * @param handler	fn(原数据)
     * @return
     *
     * @author 延晓磊
     *
     * @since 2020年8月10日
     */
    default Result handleByShop(Serializable id, Long shopId, Function<T,Result> handler) {
        // 查询原数据
        T dbData = this.getByShop(id, shopId);
        if (dbData == null) {
            return Result.ERROR_DATA_NULL;
        }
        // 处理
        return handler.apply(dbData);
    }

    /**
     * ID查询，返回指定字段
     *
     * @param id
     * @return
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default T getById(Serializable id, String... columns) {
        ParamEmptyUtil.checkNull(id);
        return this.getFirst(new Query<T>().select(columns).eq("id", id));
    }

    /**
     * 获取首个
     *
     * @param query 条件+排序
     * @return
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default T getFirst(QueryWrapper<T> query) {
        return CollectionUtil.first(this.list(ObjectUtil.toDefault(query, new Query<T>()).last("LIMIT 1")));
    }

    /**
     * 根据前缀，获取序列ID
     *
     * @param idPrefix
     * @return
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default String getLastId(String idPrefix){
        T o = this.getFirst(new Query<T>().select(MapperConstant.FieldName.ID).likeLeft(MapperConstant.FieldName.ID, idPrefix).descId());
        return o == null ? null : (String)ReflectFieldUtil.get(o, MapperConstant.FieldName.ID);
    }

    /**
     * 全部分页查询，返回Pager
     *
     * @param pageNum		页码：null/0 == 首页，负数 == 尾页，大于页数 == 尾页
     * @param pageSize  	页长：null/0/负数 == 10，最大10000
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Integer pageNum, Integer pageSize, OrderItem ...orders) {
        return MybatisPlusUtil.toPager(page(MybatisPlusUtil.toPage(pageNum, pageSize, orders)));
    }

    /**
     * 全部分页查询，返回Pager
     *
     * @param page
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(PageNumSize page, OrderItem ...orders) {
        return this.findPage(page.getPageNum(), page.getPageSize(), orders);
    }

    /**
     * 全部分页查询，返回Pager
     *
     * @param pageNum		页码：null/0 == 首页，负数 == 尾页，大于页数 == 尾页
     * @param pageSize  	页长：null/0/负数 == 10，最大10000
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Integer pageNum, Integer pageSize, List<OrderItem> orders) {
        return MybatisPlusUtil.toPager(page(MybatisPlusUtil.toPage(pageNum, pageSize, orders)));
    }

    /**
     * 全部分页查询，返回Pager
     *
     * @param page
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(PageNumSize page, List<OrderItem> orders) {
        return this.findPage(page.getPageNum(), page.getPageSize(), orders);
    }

    /**
     * 分页排序查询，返回Pager
     *
     * @param query         查询条件
     * @param pageNum		页码：null/0 == 首页，负数 == 尾页，大于页数 == 尾页
     * @param pageSize  	页长：null/0/负数 == 10，最大10000
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Wrapper<T> query, Integer pageNum, Integer pageSize, OrderItem ...orders) {
        return MybatisPlusUtil.toPager(page(MybatisPlusUtil.toPage(pageNum, pageSize, orders), query));
    }

    /**
     * 分页排序查询，返回Pager
     *
     * @param query         查询条件
     * @param page
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Wrapper<T> query, PageNumSize page, OrderItem ...orders) {
        return this.findPage(query, page.getPageNum(), page.getPageSize(), orders);
    }

    /**
     * 分页排序查询，返回Pager
     *
     * @param query         查询条件
     * @param pageNum		页码：null/0 == 首页，负数 == 尾页，大于页数 == 尾页
     * @param pageSize  	页长：null/0/负数 == 10，最大10000
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Wrapper<T> query, Integer pageNum, Integer pageSize, List<OrderItem> orders) {
        return MybatisPlusUtil.toPager(page(MybatisPlusUtil.toPage(pageNum, pageSize, orders), query));
    }

    /**
     * 分页排序查询，返回Pager
     *
     * @param query         查询条件
     * @param page
     * @param orders        排序
     * @return 分页对象
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default Pager<T> findPage(Wrapper<T> query, PageNumSize page, List<OrderItem> orders) {
        return this.findPage(query, page.getPageNum(), page.getPageSize(), orders);
    }

    /**
     * 自定义搜索（需要XML支持）
     *
     * @param query 查询参数：字段、条件、分组、排序等，支持子查询
     * @return
     *
     * @author 延晓磊
     * @since 2021年11月25日
     */
    List<T> list(MysqlQuery query);

    /**
     * ID查询，返回指定字段
     *
     * @param idList
     * @return
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default List<T> listByIds(Collection<? extends Serializable> idList, String... columns) {
        if (ArrayUtils.isEmpty(columns)) {
            return this.listByIds(idList);
        }
        if (CollectionUtils.isEmpty(idList)) {
            return new ArrayList<>();
        }
        return this.list(new Query<T>().select(columns).ids(idList));
    }

    /**
     * Map查询
     *
     * @param query
     * @param keyGetter
     * @return {key:o}
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default <K extends Serializable> Map<K,T> map(Wrapper<T> query, Function<T,K> keyGetter){
        return CollectionUtil.toMap(list(query), keyGetter);
    }

    /**
     * Map查询
     *
     * @param query
     * @param keyGetter
     * @return {key:o}
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default <K extends Serializable, V> Map<K,V> map(Wrapper<T> query, Function<T,K> keyGetter, Function<T,V> valueGetter){
        return CollectionUtil.toMap(list(query), keyGetter, valueGetter);
    }

    /**
     * ID查询，返回Map
     *
     * @param idList
     * @return {id:o}
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default <K extends Serializable> Map<K,T> mapByIds(Collection<K> idList){
        return mapByIds(idList, o -> (K)ReflectFieldUtil.get(o, MapperConstant.FieldName.ID));
    }

    /**
     * ID查询，返回Map
     *
     * @param idList
     * @param keyGetter
     * @return {key:o}
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default <K extends Serializable> Map<K,T> mapByIds(Collection<? extends Serializable> idList, Function<T,K> keyGetter){
        return CollectionUtil.toMap(listByIds(idList), keyGetter);
    }

    /**
     * ID查询，返回Map
     *
     * @param idList
     * @param keyGetter
     * @param columns
     * @return {key:o}
     *
     * @author 延晓磊
     * @since 2021年08月27日
     */
    default <K extends Serializable> Map<K,T> mapByIds(Collection<? extends Serializable> idList, Function<T,K> keyGetter, String... columns){
        return CollectionUtil.toMap(listByIds(idList, columns), keyGetter);
    }

    /**
     * 查询单个字段
     *
     *  注意：如果反射的属性继承至父类，将报错 MybatisPlusException: can not find lambda cache for this entity [……]
     *
     * @param query     查询条件
     * @param getter    字段值的get方法
     * @param <V>       字段值的类型
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    @SuppressWarnings("unchecked")
    default <V> List<V> findColumnList(Query<T> query, SFunction<T,V> getter){
        return this.list(MybatisPlusUtil.toLambda(query).select(getter)).stream().map(getter).collect(Collectors.toList());
    }

    /**
     * 查询单个字段
     *
     *  场景：因为反射的属性继承至父类，findColumnList(query,getter)报错时，可用本方便替代
     *
     * @param query     查询条件
     * @param column    字段名
     * @param getter    字段值的get方法
     * @param <V>       字段值的类型
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    default <V> List<V> findColumnList(QueryWrapper<T> query, String column, SFunction<T,V> getter){
        return this.list(query.select(column)).stream().map(getter).collect(Collectors.toList());
    }

    /**
     * 查询单个字段
     *
     *  注意：如果反射的属性继承至父类，将报错 MybatisPlusException: can not find lambda cache for this entity [……]
     *
     * @param query     查询条件
     * @param getter    字段值的get方法
     * @param <V>       字段值的类型
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    @SuppressWarnings("unchecked")
    default <V> Set<V> findColumnSet(Query<T> query, SFunction<T,V> getter){
        return this.list(MybatisPlusUtil.toLambda(query).select(getter)).stream().map(getter).collect(Collectors.toSet());
    }

    /**
     * 查询单个字段
     *
     *  场景：因为反射的属性继承至父类，findColumnSet(query,getter)报错时，可用本方便替代
     *
     * @param query     查询条件
     * @param column    字段名
     * @param getter    字段值的get方法
     * @param <V>       字段值的类型
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    default <V> Set<V> findColumnSet(QueryWrapper<T> query, String column, SFunction<T,V> getter){
        return this.list(query.select(column)).stream().map(getter).collect(Collectors.toSet());
    }

    /**
     * 查询两个字段
     *
     * 注意：如果反射的属性继承至父类，将报错 MybatisPlusException: can not find lambda cache for this entity [……]
     *
     * @param query 查询条件
     * @param keyGetter KEY字段值的get方法
     * @param valueGetter   VALUE字段值的get方法
     * @param <K>
     * @param <V>
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    @SuppressWarnings("unchecked")
    default <K,V> Map<K,V> findColumnMap(Query<T> query, SFunction<T,K> keyGetter, SFunction<T,V> valueGetter){
        return this.list(MybatisPlusUtil.toLambda(query).select(keyGetter, valueGetter)).stream().collect(Collectors.toMap(keyGetter, valueGetter));
    }

    /**
     * 查询两个字段
     *
     * 场景：因为反射的属性继承至父类，导致调用findColumnMap(query,kFn,vFn)报错时，可用本方便替代
     *
     * @param query 查询条件
     * @param key   KEY字段名
     * @param val   VAL字段名
     * @param keyGetter     KEY字段值的get方法
     * @param valueGetter   VALUE字段值的get方法
     * @param <K>
     * @param <V>
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    default <K,V> Map<K,V> findColumnMap(QueryWrapper<T> query, String key, String val, SFunction<T,K> keyGetter, SFunction<T,V> valueGetter){
        return this.list(query.select(key, val)).stream().collect(Collectors.toMap(keyGetter, valueGetter));
    }

    /**
     * 查询ID字段 - 反射getter，建议使用findColumnList
     *
     * @param query     查询条件
     * @param <V>       ID的类型
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    @SuppressWarnings("unchecked")
    default <V> List<V> findIdList(QueryWrapper<T> query){
        if (query == null) {
            query = Wrappers.query();
        }
        return this.list(query.select(MapperConstant.ColumnName.ID)).stream().map(o -> (V)ReflectFieldUtil.get(o, MapperConstant.FieldName.ID)).collect(Collectors.toList());
    }




    /**
     * 判断是否存在
     *
     * @param id
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    default boolean exists(Serializable id){
        if (id == null) {
            throw ParamRuntimeException.NULL;
        }
        return this.exists(new Query<T>().eq("id", id));
    }

    /**
     * 判断是否存在
     *
     * @param query 查询条件（必须带泛型，不然会调用exists#Serializable）
     * @return
     *
     * @author 延晓磊
     * @since 2021年09月09日
     */
    default boolean exists(QueryWrapper<T> query){
        return this.count(query) > 0;
    }

    /**
     * 求和
     *
     * @param query 查询条件
     * @param column  求和字段
     * @return BigDecimal
     *
     * @author 延晓磊
     * @since 2022年02月07日
     */
    default BigDecimal sum(QueryWrapper<T> query, String column){
        return DecimalUtil.valueOf(sumToStr(query, column));
    }

    /**
     * 求和，针对amount字段
     *
     * @param query 查询条件
     * @return BigDecimal
     *
     * @author 延晓磊
     * @since 2022年02月07日
     */
    default BigDecimal sum(QueryWrapper<T> query){
        return sum(query, MapperConstant.FieldName.AMOUNT);
    }

    /**
     * 求和
     *
     * @param query 查询条件
     * @param column  求和字段
     * @return String
     *
     * @author 延晓磊
     * @since 2022年02月07日
     */
    default String sumToStr(QueryWrapper<T> query, String column){
        return this.getObj(query.select(String.format(MysqlConstant.GroupFunction.SUM_ZERO, column)), StringUtil::toString);
    }

    /**
     * 求和，针对amount字段
     *
     * @param query 查询条件
     * @return String
     *
     * @author 延晓磊
     * @since 2022年02月07日
     */
    default String sumToStr(QueryWrapper<T> query){
        return sumToStr(query, MapperConstant.FieldName.AMOUNT);
    }

    /**
     * 批量查询和处理
     *
     * @param size  分页长度
     * @param consumer 批量处理器（不支持多数据源切换）
     *
     * @author 延晓磊
     * @since 2022年11月15日
     */
    default void batch(int size, Consumer<List<T>> consumer){
        int i = 1;
        List<T> list;
        do {
            // 分页查询和处理
            list = this.findPage(i, size).getList();
            consumer.accept(list);
            i++;
        } while (list.size() >= size);
    }

    /**
     * 批量查询和处理
     *
     * @param query 查询条件/排序条件
     * @param size  分页长度
     * @param consumer 批量处理器（不支持多数据源切换）
     *
     * @author 延晓磊
     * @since 2022年11月15日
     */
    default void batch(QueryWrapper<T> query, int size, Consumer<List<T>> consumer){
        int i = 1;
        List<T> list;
        do {
            // 分页查询和处理
            list = this.findPage(query, i, size).getList();
            consumer.accept(list);
            i++;
        } while (list.size() >= size);
    }
}
